package main

import "fmt"

type node[T comparable] struct {
	data T
	prev *node[T]
	next *node[T]
}

type list[T comparable] struct {
	head, tail *node[T]
	len        int
}

func (l *list[T]) IsEmpty() bool {
	return l.head == nil && l.tail == nil
}

func (l *list[T]) Add(data T) {
	n := &node[T]{
		data: data,
		prev: nil,
		next: l.head,
	}

	if l.IsEmpty() {
		l.head = n
		l.tail = n
	} else {
		l.head.prev = n
		l.head = n
	}

}

func (l *list[T]) Push(data T) {
	n := &node[T]{
		data: data,
		prev: l.tail,
		next: nil,
	}

	if l.IsEmpty() {
		l.head = n
		l.tail = n
	} else {
		l.tail.next = n
		l.tail = n
	}

}

func (l *list[T]) Remove(data T) {
	for p := l.head; p != nil; p = p.next {
		if data == p.data {
			if p == l.head {
				l.head = p.next
			}

			if p == l.tail {
				l.tail = p.prev
			}

			if p.prev != nil {
				p.prev.next = p.next
			}
			if p.next != nil {
				p.next.prev = p.prev
			}
			return
		}
	}
}

func (l *list[T]) Print() {
	if l.IsEmpty() {
		fmt.Println("The link is empty.")
		return
	}

	for p := l.head; p != nil; p = p.next {
		fmt.Printf("[%v] -> ", p.data)
	}
	fmt.Println("nil")
}

func main() {
	var l = list[int]{}
	l.Add(1)
	l.Add(2)

	l.Push(3)
	l.Push(4)

	l.Add(5)
	l.Add(3)
	l.Add(4)

	l.Push(8)
	l.Push(0)
	l.Push(10)
	l.Print()

	l.Remove(5)
	l.Remove(3)
	l.Remove(1)
	l.Print()
}
